
// @ts-check
import React, { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import cs from 'classnames'
import styles from './style.less'

/** @type {React.FC<import('./type').ScorllViewProps>} */
const ScorllView = ({
  children,
  className = '',
  renderTopContent = () => null
}) => {
  const [isOverflow, setIsOverflow] = useState(false)
  const [containerHeight, setContainerHeight] = useState(0)
  const [contentHeight, setContentHeight] = useState(0)
  const [barHeight, setBarHeight] = useState(0)
  const [barTop, setBarTop] = useState(0)
  const [wholeBarHeight, setWholeBarHeight] = useState(0)
  const [contentScorllTop, setContentScorllTop] = useState(0)
  const lastContentHeight = useRef(0)
  const lastContentScorllTop = useRef(0)
  const lastBarTop = useRef(0)
  // 监听变化
  const containerRef = useCallback((/** @type {HTMLDivElement | null} */ref) => {
    if(ref) {
      setContainerHeight(ref.offsetHeight)
    }
  }, [])
  const contentRef = useCallback((/** @type {HTMLDivElement | null} */ref) => {
    if(ref) {
      setContentHeight(ref.offsetHeight)
    }
  }, [])
  const wholeBarRef = useCallback((/** @type {HTMLDivElement | null} */ref) => {
    if(ref) {
      setWholeBarHeight(ref.offsetHeight)
    }
  }, [])


  const onMouseDown = useCallback((e) => {
    const  {
      height,
      top
    } = e.currentTarget.getBoundingClientRect()
    const bufferTop = height - wholeBarHeight + 40
    const maxTop = wholeBarHeight - barHeight
    let distance = e.nativeEvent.clientY - top - bufferTop
    distance = distance > maxTop ? maxTop : distance <= 0 ? 0 : distance
    setBarTop(distance)
    e.currentTarget.onmousemove = (e) => {
      let distance = e.clientY - top - bufferTop
      distance = distance > maxTop ? maxTop : distance <= 0 ? 0 : distance
      setBarTop(distance)
    }
  }, [barHeight, wholeBarHeight])
  const onMouseLeave = useCallback((e) => {
    e.currentTarget.onmousemove = null
  }, [])
  const onMouseUp = useCallback((e) => {
    e.currentTarget.onmousemove = null
  }, [])
  const onWheel = useCallback((e) => {
    const  {
      height,
    } = e.currentTarget.getBoundingClientRect()
    if(height >= contentHeight) return
    // 触发一次滚动距离
    const offset = 15 
    const maxTop = wholeBarHeight - barHeight
    let distance = e.deltaY > 0 ? (barTop + offset) : (barTop - offset)
    distance = distance > maxTop ? maxTop : distance <= 0 ? 0 : distance
    setBarTop(distance)
  }, [barTop, barHeight, contentHeight, wholeBarHeight])

  // 内容高度变化
  useEffect(() => {
    if(containerHeight > 0) {
      const isScorlling = lastBarTop.current != barTop
      const isOverflow = contentHeight > containerHeight
      // 是否溢出
      setIsOverflow(isOverflow)
      /**
       * 计算滚动条高度
       * 
       * containerHeight                barHeight
       * ________________  =  ___________________________
       *  contentHeight              wholeBarHeight
       */
      const barHeight = (wholeBarHeight * containerHeight) / contentHeight
  
      /**
       * 计算内容滚动距离
       * 
       * ContentScorllTop                barTop
       * ________________  =  ___________________________
       *  contentHeight             wholeBarHeight
       */
      let contentScorllTop = barTop * contentHeight / wholeBarHeight
  
      const maxTop = wholeBarHeight - barHeight
      // 内容是否增长了
      if(!isScorlling) {
        // 静止转态下才重新计算
        const growHeight = contentHeight - lastContentHeight.current
        const isGrow = growHeight > 0
        if(isGrow) {
          // 从新计算距离 保证当前位置不变
          contentScorllTop = lastContentScorllTop.current
        }else {
          // 高度减少
          if(!isOverflow) {
            // 没有溢出
            contentScorllTop = 0
            setBarTop(0)
          }else {
            if(barTop > maxTop) {
              // 当前BarTop超出了最大值
              contentScorllTop = 0
              setBarTop(0)
            }else {
              contentScorllTop = lastContentScorllTop.current
            }
          }
        }
      }
  
      setContentScorllTop(contentScorllTop)
      setBarHeight(barHeight)
      lastContentHeight.current = contentHeight
      lastContentScorllTop.current = contentScorllTop
      lastBarTop.current = barTop
    }
  }, [barTop, containerHeight, contentHeight])



  return <>
    <div
      onWheel = { onWheel }     
      className = { cs(styles['container'], className) } 
      ref = { (ref) => { containerRef(ref) } }>

      {/* 内容 */}
      <div ref = { ref => { contentRef(ref) } } className = {styles['content']}>
        <div className = { styles['scorllContent'] } style = {{top: `${-contentScorllTop}px`}}>
          { children }
        </div>
      </div>
      {/* 滚动条 */}
      <div 
        onMouseLeave = { onMouseLeave }
        onMouseUp = { onMouseUp }
        onMouseDown = { onMouseDown }
        className = {styles['barBuffer']} 
        style={{visibility: isOverflow ? 'visible' : 'hidden'}}>
        <div 
          ref = {ref => wholeBarRef(ref)}
          className = {styles['barOuter']} >
          <div 
            className = { styles['barInner'] } 
            style = {{ height: `${barHeight}px`, top: `${barTop}px`}}></div>
        </div>
      </div>
      {/* 顶部 */}
      <div className = {styles['topContent']}>
        {
          renderTopContent()
        }
      </div>
    </div>
  </>
}
export default ScorllView
